import 'dart:async';

import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';
import 'package:just_audio/just_audio.dart';
import 'package:vibration/vibration.dart';
import 'package:xindong/common/xindong_all.dart';
import 'package:xindong/common/xindong_methods.dart';
import 'package:xindong/manager/xindong_chat_singleton.dart';
import 'package:xindong/manager/xindong_page_singleton.dart';
import 'package:xindong/manager/xindong_permission_ctr.dart';
import 'package:xindong/manager/xindong_tencent_singleton.dart';
import 'package:xindong/manager/xindong_user_controller.dart';
import 'package:xindong/models/chat_message/xindong_signal_message.dart';
import 'package:xindong/models/user/xindong_user.dart';
import 'package:xindong/res/assets_res.dart';
import 'package:xindong/routes/call/controller/xindong_call_handle.dart';
import 'package:xindong/routes/call/controller/xindong_call_rtc_controller.dart';
import 'package:xindong/routes/call/dialog/xindong_call_answer_dialog.dart';
import 'package:xindong/routes/call/dialog/xindong_call_mini_audio_dialog.dart';
import 'package:xindong/routes/call/dialog/xindong_call_mini_video_dialog.dart';
import 'package:xindong/routes/call/dialog/xindong_call_waiting_dialog.dart';
import 'package:xindong/routes/call/xindong_call_yemian.dart';
import 'package:xindong/routes/conversation/model/xindong_liaotian_detail_class.dart';
import 'package:xindong/utils/xindong_conversion_util.dart';
import 'package:xindong/utils/xindong_text_util.dart';
import 'package:tencent_cloud_chat_uikit/tencent_cloud_chat_uikit.dart';

class XinDongActionStatus {
  static const int cancel = 0;
  static const int hangup = 0;
  static const int reject = 0;
  static const int accept = 1;
  static const int permission = 2;
}

class XinDongCallType {
  static const int unknown = 0;
  static const int audioCall = 1;
  static const int videoCall = 2;
}

enum XinDongCallStatus {
  none,
  connecting,
  active,
  minimize,
}

enum XinDongCallSupport {
  none,
  onlyAudio,
  onlyVideo,
  all,
}

class XinDongCallController extends GetxController {
  static XinDongCallController get to => Get.find();

  static const int timeout = 60;

  final AudioPlayer _audioPlayer = AudioPlayer();
  final _callingHandle = XinDongCallHandle();

  late bool isMineInvite = false;
  late XinDongUser targetUser;

  XinDongUser get mineUser => XinDongUserController.to.callUser;

  XinDongCallStatus status = XinDongCallStatus.none;

  int currentCallType = XinDongCallType.unknown;

  int? targetVersion;
  String inviterID = "0";
  String currentInviteID = "";
  dynamic currentOrderId = "";

  Timer? connectTimer;
  int connectingTimeCount = 0;

  bool operationWaiting = false;

  bool _isInCallingPage = false;

  String showStatusTipsText = "";

  String showConnectingText = "";

  String get callingTypeText {
    if (currentCallType == XinDongCallType.audioCall) {
      return "语音通话";
    } else if (currentCallType == XinDongCallType.videoCall) {
      return "视频通话";
    }
    return "";
  }

  dynamic currentPrice = "0";

  bool get isConsumer {
    return mineUser.isMale;
  }

  // 0 不请求callTime，1请求callTime
  int timer_switch = 0;

  XinDongCallSupport callSupport(XinDongLiaoTianDetailClass model) {
    if (model.another.isFemale) {
      if (model.another.video_switch == 1 && model.another.voice_switch == 1) {
        return XinDongCallSupport.all;
      }
      if (model.another.voice_switch == 1) {
        return XinDongCallSupport.onlyAudio;
      }
      if (model.another.video_switch == 1) {
        return XinDongCallSupport.onlyVideo;
      }
    } else {
      final user = XinDongUserController.to.userInfo!;
      if (user.configPersonal?.video_switch == 1 && user.configPersonal?.voice_switch == 1) {
        return XinDongCallSupport.all;
      }
      if (user.configPersonal?.voice_switch == 1) {
        return XinDongCallSupport.onlyAudio;
      }
      if (user.configPersonal?.video_switch == 1) {
        return XinDongCallSupport.onlyVideo;
      }
    }
    return XinDongCallSupport.none;
  }

  void setup() {
    XinDongCallRTCController.to.init();
  }

  static requestCall(XinDongUser user, {int callType = XinDongCallType.videoCall, bool autoCall = false}) async {
    XinDongCallController provider = XinDongCallController.to;
    provider.currentCallType = callType;
    provider.showStatusTipsText = autoCall ? "自动呼叫…" : "";
    provider.inviterID = XinDongUserController.to.loginId!;
    provider.targetUser = user;
    try {
      if (provider.status != XinDongCallStatus.none) {
        showToast("正在通话中");
        return;
      }
    } catch (e) {
      mCatchPrint(e);
    }
    await provider._call(user, callType);
  }

  static showPage({required XinDongUser user, required int callType, required bool isMineInvite}) async {
    XinDongCallController provider = XinDongCallController.to;
    provider.currentCallType = callType;
    provider.status = XinDongCallStatus.connecting;
    provider.targetUser = user;
    provider.isMineInvite = isMineInvite;
    provider.showConnectingText =
        provider.isMineInvite ? "正在接通${provider.callingTypeText}…" : "对方正在邀请你进行${provider.callingTypeText}…";
    provider.createTimer();
    if (isMineInvite) {
      FocusScope.of(navigatorKey.currentContext!).requestFocus(FocusNode());
      XinDongCallWaitingDialog.instance.show(
        onReject: provider.reject,
        onCancel: provider.cancel,
        onAccept: () async {
          bool accept = await provider.accept();
          if (!accept) {
            return;
          }
          XinDongCallWaitingDialog.instance.hide();
        },
      );
    } else {
      XinDongCallAnswerDialog.instance.show(
        onReject: provider.reject,
        onAccept: () async {
          bool acceptSuccess = await provider.accept();
          if (!acceptSuccess) {
            return;
          }
          XinDongCallAnswerDialog.instance.hide();
        },
      );
    }
    try {
      XinDongAll.instance.pauseAllPlayer();
      provider.playRingingTone();
      provider.vibrateCallingConnecting();
    } catch (e) {
      mCatchPrint(e);
    }
  }

  static Future<void> close({bool needDelay = true}) async {
    FocusScope.of(navigatorKey.currentContext!).requestFocus(FocusNode());
    final controller = XinDongCallController.to;
    controller.stopRingingTone();
    controller.destroyTimer();
    XinDongCallRTCController.to.exitRoom();
    if (controller.status == XinDongCallStatus.active || controller._isInCallingPage) {
      controller._isInCallingPage = false;
      controller.exitPage();
    } else {
      await Future.delayed(needDelay ? const Duration(milliseconds: 1500) : Duration.zero);
    }
    if (controller.status == XinDongCallStatus.none) {
      return Future.value();
    }
    XinDongChatSingleton.instance.directTrigger();
    XinDongUserController.to.requestUserVolley();

    XinDongCallAnswerDialog.instance.hide();
    XinDongCallWaitingDialog.instance.hide();
    XinDongCallMiniVideoDialog.instance.hide();
    XinDongCallMiniAudioDialog.instance.hide();
    controller.status = XinDongCallStatus.none;
    controller.operationWaiting = false;
    controller.currentInviteID = "";
    controller.currentOrderId = "";
    controller.currentCallType = XinDongCallType.unknown;
    return Future.value();
  }

  void startChat() {
    FocusScope.of(navigatorKey.currentContext!).requestFocus(FocusNode());
    if (status == XinDongCallStatus.active) {
      return;
    }
    destroyTimer();
    status = XinDongCallStatus.active;
    update();

    pushPage();
    XinDongCallAnswerDialog.instance.hide();
    XinDongCallWaitingDialog.instance.hide();
    XinDongCallMiniVideoDialog.instance.hide();
    XinDongCallMiniAudioDialog.instance.hide();

    XinDongCallRTCController.to.enterRoom(orderId: currentOrderId);
  }

  void createTimer() {
    connectingTimeCount = 0;
    connectTimer?.cancel();
    connectTimer = null;
    connectTimer = Timer.periodic(const Duration(seconds: 1), (timer) async {
      if (status == XinDongCallStatus.connecting) {
        connectingTimeCount++;
        update();
      }
    });
  }

  void destroyTimer() {
    connectTimer?.cancel();
    connectTimer = null;
  }

  String _createSignalData(int callType, {bool isBusy = false, bool isCallEnd = false}) {
    XinDongSignalMessage model = XinDongSignalMessage();
    model.type = callType;
    model.isBusy = isBusy;
    model.isCallEnd = isCallEnd;
    model.orderid = currentOrderId;
    model.price = currentPrice;
    model.user = mineUser;
    return XinDongConversionUtil.modelToDecodeJson(model) ?? "";
  }

  Future<bool> checkPermission(int callType) async {
    if (callType == XinDongCallType.videoCall) {
      bool havePermission = await XinDongPermissionCtr.checkPermission(XinDongAppPermission.camera);
      if (havePermission == false) {
        return false;
      }
    }
    bool microphonePermission = await XinDongPermissionCtr.checkPermission(XinDongAppPermission.microphone);
    if (microphonePermission == false) {
      return false;
    }
    return true;
  }

  Future<void> _call(XinDongUser user, int callType) async {
    try {
      operationWaiting = false;
      var permission = await checkPermission(callType);
      if (!permission) {
        showToast("请打开权限才能呼叫哦！");
        return Future.value();
      }

      status = XinDongCallStatus.connecting;
      final checkResult = await _callingHandle.xindong_checkCall(userId: user.uid, callType: callType);
      if (!checkResult) {
        status = XinDongCallStatus.none;
        return Future.value();
      }
      String dataString = _createSignalData(callType);
      V2TimValueCallback<String> result = await TencentImSDKPlugin.v2TIMManager.v2timSignalingManager.invite(
        invitee: user.uid.toString(),
        data: dataString,
        timeout: timeout,
        offlinePushInfo: OfflinePushInfo(title: callingTypeText, desc: "${mineUser.getName}邀请您$callingTypeText了！"),
      );
      if (result.code == 0) {
        currentInviteID = result.data!;
        XinDongCallController.showPage(user: user, isMineInvite: true, callType: callType);
      } else {
        status = XinDongCallStatus.none;
        XinDongTencentSingleton.instance.errorCode(result, targetUser);
      }
    } catch (error) {
      mCatchPrint(error);
    }

    return Future.value();
  }

  Future<bool> accept({bool needRequest = true}) async {
    var permission = await checkPermission(currentCallType);
    if (!permission) {
      showToast("请打开权限才能接听哦！");
      return false;
    }
    changeOperationWaitingStatus(true);
    if (needRequest) {
      final statusResult = await _callingHandle.answerWithStatus(status: XinDongActionStatus.accept);
      if (!statusResult) {
        changeOperationWaitingStatus(false);
        return false;
      }
    }

    String dataString = _createSignalData(currentCallType);
    V2TimCallback result = await TencentImSDKPlugin.v2TIMManager.getSignalingManager().accept(
          inviteID: currentInviteID,
          data: dataString,
        );
    if (result.code != 0) {
      changeOperationWaitingStatus(false);
      if (result.code != 8010) {
        showToast(result.desc);
      }
    }
    XinDongCallController.to.stopRingingTone();
    return true;
  }

  Future<void> reject({String? busyInviteID}) async {
    changeOperationWaitingStatus(true);

    final statusResult = await _callingHandle.xindong_callCancel();
    if (!statusResult) {
      changeOperationWaitingStatus(false);
      return Future.value();
    }
    bool isBusy = !TextUtil.isEmpty(busyInviteID);
    String dataString = _createSignalData(currentCallType, isBusy: isBusy);
    V2TimCallback result = await TencentImSDKPlugin.v2TIMManager.getSignalingManager().reject(
          inviteID: isBusy ? busyInviteID! : currentInviteID,
          data: dataString,
        );
    if (result.code != 0) {
      changeOperationWaitingStatus(false);
      if (result.code != 8010) {
        showToast(result.desc);
      }
    }
    if (isBusy == false) {
      XinDongCallController.close();
    }
    XinDongCallController.to.stopRingingTone();
    return Future.value();
  }

  void timeoutCancel() {
    _callingHandle.xindong_callCancel();
  }

  Future<void> cancel() async {
    changeOperationWaitingStatus(true);
    final statusResult = await _callingHandle.xindong_callCancel();
    if (!statusResult) {
      changeOperationWaitingStatus(false);
      return Future.value();
    }
    String dataString = _createSignalData(currentCallType);
    V2TimCallback result = await TencentImSDKPlugin.v2TIMManager.getSignalingManager().cancel(
          inviteID: currentInviteID,
          data: dataString,
        );
    if (result.code != 0) {
      changeOperationWaitingStatus(false);
      if (result.code != 8010) {
        showToast(result.desc);
      }
    }
    XinDongCallController.close();
    return Future.value();
  }

  Future<void> hangup() async {
    if (status == XinDongCallStatus.none) {
      XinDongCallController.close();
      return Future.value();
    }
    if (operationWaiting == true) {
      return Future.value();
    }

    changeOperationWaitingStatus(true);

    // 嵌套方法
    Future sendSignaling() {
      String dataString = _createSignalData(currentCallType, isCallEnd: true);
      return TencentImSDKPlugin.v2TIMManager.v2timSignalingManager.invite(
        invitee: targetUser.uid.toString(),
        data: dataString,
        timeout: 0,
      );
    }

    //
    // await Future.wait([
    // ]);
    try {
      _callingHandle.answerWithStatus(status: XinDongActionStatus.hangup);
      sendSignaling();
    } catch (e) {}
    XinDongCallController.close(needDelay: false);
    changeOperationWaitingStatus(false);
    return Future.value();
  }

  void changeOperationWaitingStatus(bool isWaiting) {
    operationWaiting = isWaiting;
    update();
  }
}

extension XinDongCallPageManager on XinDongCallController {
  void pushPage() {
    _isInCallingPage = true;
    XinDongPageSingleton.instance.pushNoBackYeMian(const XinDongCallYeMian());
  }

  Future<void> exitPage() async {
    if (XinDongCallController.to.status == XinDongCallStatus.minimize) {
      return;
    }
    XinDongPageSingleton.instance.popUntilYeMian("/XinDongCallYeMian");
  }
}

extension XinDongCallSignalingListener on XinDongCallController {
  Future<void> onReceiveNewInvitation(
      String inviteID, String inviter, String groupID, List<String> inviteeList, String data) async {
    mDebugPrint("onReceiveNewInvitation $inviteID $inviter");
    if (currentInviteID == inviteID) {
      return;
    }
    XinDongSignalMessage model = XinDongConversionUtil.decodeJsonToSignalClass(data);
    if (!XinDongSignalMessage.isCalling(model)) {
      return;
    }
    mDebugPrint("onReceiveNewInvitation ${model.toJson()}");
    try {
      if (model.type == XinDongCallType.audioCall || model.type == XinDongCallType.videoCall) {
        if (model.isCallEnd == true) {
          changeOperationWaitingStatus(true);
          XinDongCallController.close(needDelay: false);
          return;
        }
        if (status != XinDongCallStatus.none) {
          reject(busyInviteID: inviteID);
          return;
        }
        showStatusTipsText = "";
        inviterID = inviter;
        currentInviteID = inviteID;
        currentOrderId = model.orderid;
        currentPrice = model.price;
        targetUser = model.user!;
        targetVersion = model.version;
        operationWaiting = false;
        XinDongCallController.showPage(user: model.user!, callType: model.type, isMineInvite: false);
      } else {
        stopRingingTone();
      }
    } catch (error) {
      mCatchPrint(error);
    }
  }

  void onInviteeAccepted(String inviteID, String invitee, String data) {
    mDebugPrint("onInviteeAccepted $inviteID $invitee");
    XinDongSignalMessage model = XinDongConversionUtil.decodeJsonToSignalClass(data);
    if (!XinDongSignalMessage.isCalling(model)) {
      stopRingingTone();
      return;
    }
    mDebugPrint("onInviteeAccepted ${model.toJson()}");
    stopRingingTone();
    vibrateAccept();
    changeOperationWaitingStatus(false);
    if (model.type == XinDongCallType.audioCall || model.type == XinDongCallType.videoCall) {
      currentPrice = model.price;
      currentOrderId = model.orderid;
      if (!isMineInvite) {
        targetVersion = model.version;
      }
      startChat();
    }
  }

  Future<void> onInviteeRejected(String inviteID, String invitee, String data) async {
    mDebugPrint("onInviteeRejected $inviteID $invitee");
    XinDongSignalMessage model = XinDongConversionUtil.decodeJsonToSignalClass(data);
    if (!XinDongSignalMessage.isCalling(model)) {
      stopRingingTone();
      return;
    }
    mDebugPrint("onInviteeRejected ${model.toString()}");
    if (model.type == XinDongCallType.audioCall || model.type == XinDongCallType.videoCall) {
      if (inviteID == currentInviteID) {
        playEndRingingTone();
        if (isMineInvite) {
          if (model.isBusy) {
            showStatusTipsText = "对方忙碌中，请稍后再拨";
          } else {
            showStatusTipsText = "对方拒接接听";
          }
        } else {
          showStatusTipsText = "已拒绝";
        }
        update();
        XinDongCallController.close();
      }
    } else {
      stopRingingTone();
    }
  }

  Future<void> onInvitationCancelled(String inviteID, String inviter, String data) async {
    mDebugPrint("onInvitationCancelled $inviteID $inviter $data");
    XinDongSignalMessage model = XinDongConversionUtil.decodeJsonToSignalClass(data);
    if (!XinDongSignalMessage.isCalling(model)) {
      stopRingingTone();
      return;
    }
    if (model.type == XinDongCallType.audioCall || model.type == XinDongCallType.videoCall) {
      playEndRingingTone();
      if (inviter == XinDongUserController.to.userInfo?.uid.toString()) {
        showStatusTipsText = "取消通话";
      } else {
        showStatusTipsText = "对方挂断通话";
      }
      update();
      XinDongCallController.close();
    } else {
      stopRingingTone();
    }
  }

  Future<void> onInvitationTimeout(String inviteID, List<String> inviteeList) async {
    mDebugPrint("onInvitationTimeout $inviteID $inviteeList");
    if (currentInviteID == inviteID) {
      playEndRingingTone();
      if (isMineInvite) {
        showStatusTipsText = "对方手机不在身边，未能接通";
        timeoutCancel();
      } else {
        showStatusTipsText = "您未接听";
      }
      update();
      XinDongCallController.close();
    } else {
      stopRingingTone();
    }
  }
}

extension XinDongCallRingingTone on XinDongCallController {
  Future<void> playRingingTone() async {
    await stopRingingTone();
    await _audioPlayer.setAsset(AssetsRes.CALLING_CONNECTING);
    await _audioPlayer.setVolume(1.0);
    await _audioPlayer.setLoopMode(LoopMode.one);
    await _audioPlayer.play();
  }

  Future<void> playEndRingingTone() async {
    await stopRingingTone();
    if (status != XinDongCallStatus.none) {
      await _audioPlayer.setAsset(AssetsRes.CALLING_HANGUP);
      await _audioPlayer.setVolume(0.3);
      await _audioPlayer.setLoopMode(LoopMode.off);
      await _audioPlayer.play();
    }
  }

  Future<void> stopRingingTone() async {
    return _audioPlayer.stop();
  }
}

extension XinDongCallVibrate on XinDongCallController {
  Future<void> vibrateCallingConnecting() async {
    if (isMineInvite == false) {
      if (status == XinDongCallStatus.connecting) {
        await Vibration.vibrate();
        await Future.delayed(const Duration(seconds: 2));
        vibrateCallingConnecting();
      }
    }
  }

  void vibrateAccept() {
    if (isMineInvite == true) {
      Vibration.vibrate(duration: 300);
      ;
    }
  }
}
